﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using iNet.Tasks;

namespace iNet.Media.Imaging.Filters
{
	/// <summary>
	/// 使用指定的 Kernel 進行 Correlation 運算的點陣圖濾鏡工作。
	/// </summary>
	public abstract class CorrelationFilterTask : BitmapImageFilterTask
	{
		#region ProgressInfo
		class ProgressInfo
		{
			public volatile int CompletedRows;
			public volatile bool HasError;
			public readonly int TotalRows;
			#region 建構子
			public ProgressInfo(int totalRows, bool has2DKernels)
			{
				this.TotalRows = totalRows;
			}
			#endregion
		}
		#endregion
		#region 欄位
		BitmapImageFilterChannels _Channels = BitmapImageFilterChannels.Argb;
		CorrelationKernels _Kernels;
        #endregion
		#region ApplyArgbFilter
		unsafe void ApplyArgbFilter(TaskVersion version, BitmapImageFilterData filterData, int startRow, int endRow, CorrelationKernels kernels, ProgressInfo progressInfo, SemaphoreSlim barrier)
		{
			try
			{
				//取得像素資訊
				int bytesPerPixel = filterData.BytesPerPixel;
				bool is16BitsColor = filterData.Is16BitsColor;
				bool alphaIncluded = filterData.IsAlphaIncluded;

				//取得 Kernel 資訊
				int horzKernelSize = 0;
				int vertKernelSize = 0;
				double[][] aKernel = kernels.AlphaKernel;
				if (aKernel != null)
				{
					vertKernelSize = aKernel.Length;
					if (vertKernelSize > 0 && aKernel[0] != null)
						horzKernelSize = aKernel[0].Length;
				}
				double[][] rKernel = kernels.RedKernel;
				if (vertKernelSize <= 0 && rKernel != null)
				{
					vertKernelSize = rKernel.Length;
					if (vertKernelSize > 0 && rKernel[0] != null)
						horzKernelSize = rKernel[0].Length;
				}
				double[][] gKernel = kernels.GreenKernel;
				if (vertKernelSize <= 0 && gKernel != null)
				{
					vertKernelSize = gKernel.Length;
					if (vertKernelSize > 0 && gKernel[0] != null)
						horzKernelSize = gKernel[0].Length;
				}
				double[][] bKernel = kernels.BlueKernel;
				if (vertKernelSize <= 0 && bKernel != null)
				{
					vertKernelSize = bKernel.Length;
					if (vertKernelSize > 0 && bKernel[0] != null)
						horzKernelSize = bKernel[0].Length;
				}
				int halfHorzKernelSize = (horzKernelSize / 2);
				int halfVertKernelSize = (vertKernelSize / 2);

				//確認是否需套用濾鏡
				if (horzKernelSize <= 0 || vertKernelSize <= 0)
					return;

				//套用濾鏡
				int width = filterData.PixelWidth;
				int height = filterData.PixelHeight;
				int stride = filterData.Stride;
				int rowCount = 0;
				byte* resultLine = ((byte*)filterData.ResultPixelBuffer + (startRow * stride));
				for (int y = startRow; y < endRow; ++y, resultLine += stride)
				{
					try
					{
						byte* resultPixel = resultLine;
						for (int x = 0; x < width; ++x, resultPixel += bytesPerPixel)
						{
							//套用 kernel
							double a = 0, r = 0, g = 0, b = 0;
							for (int kRow = 0, ky = (y - halfVertKernelSize); kRow < vertKernelSize; ++kRow, ++ky)
							{
								byte* coefSrcLine;
								if (ky < 0)
									coefSrcLine = ((byte*)filterData.SourcePixelBuffer - (ky * stride));
								else if (ky >= height)
									coefSrcLine = ((byte*)filterData.SourcePixelBuffer + ((2 * height - ky - 1) * stride));
								else
									coefSrcLine = ((byte*)filterData.SourcePixelBuffer + (ky * stride));
								for (int kColumn = 0, kx = (x - halfHorzKernelSize); kColumn < horzKernelSize; ++kColumn, ++kx)
								{
									byte* coefSrcPixel;
									if (kx < 0)
										coefSrcPixel = (coefSrcLine - (kx * bytesPerPixel));
									else if (kx >= width)
										coefSrcPixel = (coefSrcLine + ((2 * width - kx - 1) * bytesPerPixel));
									else
										coefSrcPixel = (coefSrcLine + (kx * bytesPerPixel));
									if (!is16BitsColor)
									{
										if (bKernel != null)
											b += (*(coefSrcPixel) * bKernel[kRow][kColumn]);
										if (gKernel != null)
											g += (*(coefSrcPixel + 1) * gKernel[kRow][kColumn]);
										if (rKernel != null)
											r += (*(coefSrcPixel + 2) * rKernel[kRow][kColumn]);
										if (aKernel != null && alphaIncluded)
											a += (*(coefSrcPixel + 3) * aKernel[kRow][kColumn]);
									}
									else
									{
										if (rKernel != null)
											r += (*(ushort*)(coefSrcPixel) * rKernel[kRow][kColumn]);
										if (gKernel != null)
											g += (*(ushort*)(coefSrcPixel + 2) * gKernel[kRow][kColumn]);
										if (bKernel != null)
											b += (*(ushort*)(coefSrcPixel + 4) * bKernel[kRow][kColumn]);
										if (aKernel != null && alphaIncluded)
											a += (*(ushort*)(coefSrcPixel + 6) * aKernel[kRow][kColumn]);
									}
								}
							}

							//儲存結果
							if (!is16BitsColor)
							{
								if (bKernel != null)
									*(resultPixel) = (byte)(b < 0 ? 0 : (b > 255 ? 255 : b));
								if (gKernel != null)
									*(resultPixel + 1) = (byte)(g < 0 ? 0 : (g > 255 ? 255 : g));
								if (rKernel != null)
									*(resultPixel + 2) = (byte)(r < 0 ? 0 : (r > 255 ? 255 : r));
								if (aKernel != null && alphaIncluded)
									*(resultPixel + 3) = (byte)(a < 0 ? 0 : (a > 255 ? 255 : a));
							}
							else
							{
								if (rKernel != null)
									*(ushort*)(resultPixel) = (ushort)(r < 0 ? 0 : (r > 65535 ? 65535 : r));
								if (gKernel != null)
									*(ushort*)(resultPixel + 2) = (ushort)(g < 0 ? 0 : (g > 65535 ? 65535 : g));
								if (bKernel != null)
									*(ushort*)(resultPixel + 4) = (ushort)(b < 0 ? 0 : (b > 65535 ? 65535 : b));
								if (aKernel != null && alphaIncluded)
									*(ushort*)(resultPixel + 6) = (ushort)(a < 0 ? 0 : (a > 65535 ? 65535 : a));
							}
						}
					}
					finally
					{
						//回報進度
						++rowCount;
						if (rowCount == 100)
						{
							lock (progressInfo)
							{
								progressInfo.CompletedRows += rowCount;
								rowCount = 0;
								this.ReportProgress(version, ((progressInfo.CompletedRows * 100.0) / progressInfo.TotalRows));
							}
						}
					}
					if (this.IsCanceling)
						return;
				}

				//回報進度
				if (rowCount > 100)
				{
					lock (progressInfo)
					{
						progressInfo.CompletedRows += rowCount;
						this.ReportProgress(version, ((progressInfo.CompletedRows * 100.0) / progressInfo.TotalRows));
					}
				}
			}
			catch
			{
				lock (progressInfo)
				{
					progressInfo.HasError = true;
				}
			}
			finally
			{
				barrier.Release();
			}
		}
        #endregion
		#region ApplyFilter
		//
        #endregion
		#region Channels
		/// <summary>
		/// 取得或設定套用濾鏡的色彩組合，預設值為 BitmapImageFilterChannels.Argb。
		/// </summary>
		public BitmapImageFilterChannels Channels
		{
			get
			{
				return _Channels;
			}
			set
			{
				this.SetInitializingParameter<BitmapImageFilterChannels>(ref _Channels, value);
			}
		}
		#endregion
		#region Kernels
		/// <summary>
		/// 取得或設定進行濾鏡運算的 Kernel 組合。
		/// </summary>
		protected CorrelationKernels Kernels
		{
			get
			{
				CorrelationKernels kernels = _Kernels;
				return (kernels != null ? kernels.Clone() : null);
			}
			set
			{
				lock (this.SyncRoot)
				{
					if (value != null)
						_Kernels = value.Clone();
					else
						_Kernels = null;
				}
			}
		}
        #endregion
	}
	/// <summary>
	/// 進行 Correlation 運算的 Kernel 集合。
	/// </summary>
	public class CorrelationKernels : ICloneable
	{
		#region 欄位
		double[][] _AlphaKernel;
		double[][] _BlueKernel;
		double[][] _GreenKernel;
		double[][] _LuminanceKernel;
		double[][] _RedKernel;
		#endregion
		#region 建構子
		/// <summary>
		/// 初始化新的 CorrelationKernels 執行個體。
		/// </summary>
		public CorrelationKernels()
		{ }
		/// <summary>
		/// 初始化新的 CorrelationKernels 執行個體。
		/// </summary>
		/// <param name="template">現有的 CorrelationKernels。</param>
		public CorrelationKernels(CorrelationKernels template)
		{
			if (template != null)
			{
				_AlphaKernel = template._AlphaKernel;
				_BlueKernel = template._BlueKernel;
				_GreenKernel = template._GreenKernel;
				_LuminanceKernel = template._LuminanceKernel;
				_RedKernel = template._RedKernel;
			}
		}
		#endregion
		#region AlphaKernel
		/// <summary>
		/// 取得或設定套用至透明度的 Kernel，Null (Visual Basic 為 Nothing) 表示透明度不套用濾鏡。
		/// </summary>
		public double[][] AlphaKernel
		{
			get
			{
				return this.CopyKernel(_AlphaKernel);
			}
			set
			{
				_AlphaKernel = this.CopyKernel(value);
			}
		}
		#endregion
		#region BlueKernel
		/// <summary>
		/// 取得或設定套用至藍色的 Kernel，Null (Visual Basic 為 Nothing) 表示藍色不套用濾鏡。
		/// </summary>
		public double[][] BlueKernel
		{
			get
			{
				return this.CopyKernel(_BlueKernel);
			}
			set
			{
				_BlueKernel = this.CopyKernel(value);
			}
		}
		#endregion
		#region Clone
		/// <summary>
		/// 建立深層副本。
		/// </summary>
		/// <returns>深層副本。</returns>
		public CorrelationKernels Clone()
		{
			return new CorrelationKernels(this);
		}
        #endregion
		#region CopyKernel
		double[][] CopyKernel(double[][] kernel)
		{
			if (kernel == null)
				return null;
			int rowCount = kernel.Length;
			double[][] clone = new double[rowCount][];
			if (rowCount > 0)
			{
				double[] firstRow = kernel[0];
				if (firstRow == null)
					throw new ArgumentException();
				clone[0] = (firstRow.Clone() as double[]);
				int columnCount = firstRow.Length;
				for (int i = rowCount - 1; i > 0; --i)
				{
					double[] row = kernel[i];
					if (row == null || row.Length != columnCount)
						throw new ArgumentException();
					clone[i] = (row.Clone() as double[]);
				}
			}
			return clone;
		}
		#endregion
		#region GreenKernel
		/// <summary>
		/// 取得或設定套用至綠色的 Kernel，Null (Visual Basic 為 Nothing) 表示綠色不套用濾鏡。
		/// </summary>
		public double[][] GreenKernel
		{
			get
			{
				return this.CopyKernel(_GreenKernel);
			}
			set
			{
				_GreenKernel = this.CopyKernel(value);
			}
		}
		#endregion
		#region ICloneable.Clone
		object ICloneable.Clone()
		{
			return this.Clone();
		}
		#endregion
		#region LuminanceKernel
		/// <summary>
		/// 取得或設定套用至光線強度的 Kernel，Null (Visual Basic 為 Nothing) 表示光線強度不套用濾鏡。
		/// </summary>
		public double[][] LuminanceKernel
		{
			get
			{
				return this.CopyKernel(_LuminanceKernel);
			}
			set
			{
				_LuminanceKernel = this.CopyKernel(value);
			}
		}
		#endregion
		#region RedKernel
		/// <summary>
		/// 取得或設定套用至紅色的 Kernel，Null (Visual Basic 為 Nothing) 表示紅色不套用濾鏡。
		/// </summary>
		public double[][] RedKernel
		{
			get
			{
				return this.CopyKernel(_RedKernel);
			}
			set
			{
				_RedKernel = this.CopyKernel(value);
			}
		}
		#endregion
		#region SetArgbKernels
		/// <summary>
		/// 將透明度、藍色、綠色及紅色設定為同一組 Kernel。
		/// </summary>
		/// <param name="kernel">指定的 Kernel。</param>
		public void SetArgbKernels(double[][] kernel)
		{
			_AlphaKernel = _RedKernel = _GreenKernel = _BlueKernel = this.CopyKernel(kernel);
		}
		#endregion
		#region SetRgbKernels
		/// <summary>
		/// 將藍色、綠色及紅色設定為同一組 Kernel。
		/// </summary>
		/// <param name="kernel">指定的 Kernel。</param>
		public void SetRgbKernels(double[][] kernel)
		{
			_RedKernel = _GreenKernel = _BlueKernel = this.CopyKernel(kernel);
		}
        #endregion
	}
}
